package com.mayee.me.core.test.starter;

import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.util.MultiValueMap;

import java.lang.reflect.Field;

/**
 * @description: controller 层的测试基类
 * @author: mayee
 * @create: 2020-12-27 08:50
 **/

@AutoConfigureMockMvc
public class MvcTestBase {

    @Autowired
    private MockMvc mvc;

    private String fetchResult(MockHttpServletRequestBuilder builder) {
        try {
            builder = builder.contentType(MediaType.APPLICATION_JSON);
            return mvc.perform(builder).andReturn().getResponse().getContentAsString();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("mockmvc 请求异常");
        }
    }

    /**
     * @param mapping
     * @Description: get请求，无参数
     * @return: java.lang.String
     * @Author: Bobby.Ma
     * @Date: 2020/11/3 12:18
     */


    protected String doGet(String mapping) {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(mapping);
        return fetchResult(builder);
    }

    protected String doGet(String mapping, HttpHeaders httpHeaders) {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(mapping).headers(httpHeaders);
        return fetchResult(builder);
    }

    /**
     * @param mapping
     * @param key
     * @param value
     * @Description: get请求，url单个参数
     * @return: java.lang.String
     * @Author: Bobby.Ma
     * @Date: 2020/11/3 12:18
     */

    protected String doGet(String mapping, String key, String... value) {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(mapping).queryParam(key, value);
        return fetchResult(builder);
    }

    /**
     * @param mapping
     * @param params  url参数
     * @Description: get请求，url多个参数
     * @return: java.lang.String
     * @Author: Bobby.Ma
     * @Date: 2020/11/3 12:19
     */

    protected String doGet(String mapping, MultiValueMap<String, String> params) {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(mapping).queryParams(params);
        return fetchResult(builder);
    }

    /**
     * @param mapping
     * @param jsonStr body参数
     * @Description: post请求，body有参数
     * @return: java.lang.String
     * @Author: Bobby.Ma
     * @Date: 2020/11/3 12:19
     */

    protected String doPost(String mapping, String jsonStr) {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post(mapping).content(jsonStr);
        return fetchResult(builder);
    }

    /**
     * @param mapping
     * @Description: put请求，无参数
     * @return: java.lang.String
     * @Author: Bobby.Ma
     * @Date: 2020/11/3 12:19
     */

    protected String doPut(String mapping) {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.put(mapping);
        return fetchResult(builder);
    }

    /**
     * @param mapping
     * @param jsonStr
     * @Description: put请求，body有参数
     * @return: java.lang.String
     * @Author: Bobby.Ma
     * @Date: 2020/11/3 12:20
     */

    protected String doPut(String mapping, String jsonStr) {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.put(mapping).content(jsonStr);
        return fetchResult(builder);
    }

    /**
     * @param mapping
     * @param params
     * @Description: put请求，url多个参数
     * @return: java.lang.String
     * @Author: Bobby.Ma
     * @Date: 2020/11/5 17:08
     */

    protected String doPut(String mapping, MultiValueMap<String, String> params) {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.put(mapping).queryParams(params);
        return fetchResult(builder);
    }

    /**
     * @param mapping
     * @param jsonStr body参数
     * @param key
     * @param value
     * @Description: put请求，url单个参数，body有参数
     * @return: java.lang.String
     * @Author: Bobby.Ma
     * @Date: 2020/11/3 12:20
     */

    protected String doPut(String mapping, String jsonStr, String key, String... value) {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.put(mapping)
                .queryParam(key, value)
                .content(jsonStr);
        return fetchResult(builder);
    }

    /**
     * @param mapping
     * @param jsonStr body参数
     * @param params  url参数
     * @Description: put请求，url多个参数，body有参数
     * @return: java.lang.String
     * @Author: Bobby.Ma
     * @Date: 2020/11/3 12:21
     */

    protected String doPut(String mapping, String jsonStr, MultiValueMap<String, String> params) {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.put(mapping)
                .queryParams(params)
                .content(jsonStr);
        return fetchResult(builder);
    }

    /**
     * @param mapping
     * @param key
     * @param value
     * @Description: delete请求，url单个参数
     * @return: java.lang.String
     * @Author: Bobby.Ma
     * @Date: 2020/11/3 12:21
     */

    protected String doDelete(String mapping, String key, String... value) {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.delete(mapping).queryParam(key, value);
        return fetchResult(builder);
    }

    /**
     * @param mapping
     * @param params  url参数
     * @Description: delete请求，url多个参数
     * @return: java.lang.String
     * @Author: Bobby.Ma
     * @Date: 2020/11/3 12:22
     */

    protected String doDelete(String mapping, MultiValueMap<String, String> params) {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.delete(mapping).queryParams(params);
        return fetchResult(builder);
    }

    /**
     * @param proxy
     * @Description: 通过被代理对象，拿到真实对象
     * @return: T
     * @Author: Bobby.Ma
     * @Date: 2020/12/11 18:22
     */

    protected <T> T getTarget(T proxy) throws Exception {
        if (!AopUtils.isAopProxy(proxy)) {
            throw new RuntimeException("目标不是被代理对象！");
        }
        if (AopUtils.isJdkDynamicProxy(proxy)) {
            return getJdkDynamicProxyTargetObject(proxy);
        } else { //cglib
            return getCglibProxyTargetObject(proxy);
        }
    }

    /**
     * @param proxy
     * @Description: CGLIB方式被代理类的获取
     * @return: T
     * @Author: Bobby.Ma
     * @Date: 2020/12/11 18:27
     */

    private <T> T getCglibProxyTargetObject(T proxy) throws Exception {
        Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
        h.setAccessible(true);
        Object dynamicAdvisedInterceptor = h.get(proxy);
        Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
        advised.setAccessible(true);
        Object target = ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
        return (T) target;
    }

    /**
     * @param proxy
     * @Description: JDK动态代理方式被代理类的获取
     * @return: T
     * @Author: Bobby.Ma
     * @Date: 2020/12/11 18:27
     */

    private <T> T getJdkDynamicProxyTargetObject(T proxy) throws Exception {
        Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
        h.setAccessible(true);
        AopProxy aopProxy = (AopProxy) h.get(proxy);
        Field advised = aopProxy.getClass().getDeclaredField("advised");
        advised.setAccessible(true);
        Object target = ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
        return (T) target;
    }

    protected void replaceSuperField(Object obj) throws NoSuchFieldException, IllegalAccessException {
        // Object 替换为 obj 的父类
        if (!(obj instanceof Object)) {
            throw new RuntimeException("不是指定继承类的子类");
        }
        Field field = Object.class.getDeclaredField("replaceFiledName");
        field.setAccessible(true);
        field.set(obj, "newFiledObject");
    }
}
